/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>

#include <internal/chip/chip_pca9545/pca9545.h>
#include <mc/dict.h>

#include "../../internal/test_utils/mock_bus.h"

namespace {

class chip_pca9545_test : public ::testing::Test {
protected:
    void SetUp() override {
        mock_bus                     = std::make_shared<test_utils::mock_bus>();
        mock_bus->block_write_result = HAS_OK;
        mock_bus->block_write_error  = "";
    }

    dev::chip_pca9545                     chip;
    std::shared_ptr<test_utils::mock_bus> mock_bus;
};

// 成功初始化并读取 ChannelNum
TEST_F(chip_pca9545_test, init_success) {
    mc::mutable_dict csr_object;
    csr_object["ChannelNum"] = static_cast<uint8_t>(3);

    EXPECT_TRUE(chip.init(csr_object));
}

// ChannelNum 缺失时也应初始化成功，保持默认值
TEST_F(chip_pca9545_test, init_without_channel_num) {
    mc::mutable_dict csr_object;
    EXPECT_TRUE(chip.init(csr_object));
}

// ChannelNum 类型错误时需要捕获异常并返回 false
TEST_F(chip_pca9545_test, init_invalid_channel_num) {
    mc::mutable_dict csr_object;
    csr_object["ChannelNum"] = std::string("invalid");

    EXPECT_FALSE(chip.init(csr_object));
}

// 传入空 bus 时应抛出异常
TEST_F(chip_pca9545_test, open_channel_with_nullptr_bus) {
    EXPECT_THROW(chip.open_channel(nullptr), mc::invalid_op_exception);
}

// 成功执行 open_channel，验证 block_write 被调用
TEST_F(chip_pca9545_test, open_channel_success) {
    mc::mutable_dict csr_object;
    csr_object["ChannelNum"] = static_cast<uint8_t>(2);
    ASSERT_TRUE(chip.init(csr_object));

    mock_bus->set_id(1);
    chip.set_left_bus(mock_bus);
    chip.set_host_bus(mock_bus);

    EXPECT_EQ(chip.open_channel(mock_bus), HAS_OK);
    ASSERT_EQ(mock_bus->block_write_calls.size(), 1U);

    const auto& call = mock_bus->block_write_calls.front();
    EXPECT_EQ(call.offset, 0);
    ASSERT_EQ(call.write_buffer.size(), 1U);
    EXPECT_EQ(call.write_buffer[0], static_cast<uint8_t>(1U << mock_bus->get_id()));
}

// block_write 返回错误码时应抛异常
TEST_F(chip_pca9545_test, open_channel_write_failed) {
    mc::mutable_dict csr_object;
    csr_object["ChannelNum"] = static_cast<uint8_t>(1);
    ASSERT_TRUE(chip.init(csr_object));

    mock_bus->set_id(0);
    mock_bus->block_write_result = HAS_ERR;
    mock_bus->block_write_error  = "i2c error";
    chip.set_left_bus(mock_bus);
    chip.set_host_bus(mock_bus);

    EXPECT_THROW(chip.open_channel(mock_bus), mc::invalid_op_exception);
    EXPECT_EQ(mock_bus->block_write_calls.size(), 1U);
}

// block_write 抛出异常时应捕获并重新抛出
TEST_F(chip_pca9545_test, open_channel_write_exception) {
    mc::mutable_dict csr_object;
    csr_object["ChannelNum"] = static_cast<uint8_t>(1);
    ASSERT_TRUE(chip.init(csr_object));

    mock_bus->set_id(2);
    mock_bus->throw_on_block_write = true;
    chip.set_left_bus(mock_bus);
    chip.set_host_bus(mock_bus);

    EXPECT_THROW(chip.open_channel(mock_bus), mc::invalid_op_exception);
    EXPECT_EQ(mock_bus->block_write_calls.size(), 1U);
}

} // namespace
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "../../../../drivers/chip/pca9545/pca9545.h"
#include "../../../../drivers/internal/manager.h"
#include "../../test_utils/root_object.h"
#include <atomic>
#include <chrono>
#include <gtest/gtest.h>
#include <mc/common.h>
#include <mc/engine.h>
#include <test_utilities/test_base.h>
#include <thread>

using namespace devmon;

namespace drivers {
namespace chip {
namespace pca9545 {
namespace test {

class pca9545_test_service : public mc::engine::service {
public:
    pca9545_test_service(const std::string& name) : mc::engine::service(name) {
    }
};

class Pca9545Test : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service = mc::make_shared<pca9545_test_service>("bmc.kepler.test_pca9545");
        m_test_service->init();
        m_test_service->start();

        // 创建根对象
        m_root_object = root_object::create_root_object(m_test_service.get());
    }

    static void TearDownTestSuite() {
        if (m_test_service) {
            m_test_service->stop();
            m_test_service.reset();
        }
        if (m_root_object) {
            m_root_object.reset();
        }
        auto& manager = dev::device_manager::get_instance();
        manager.clear_all();
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {
        // 清理可能存在的旧chip对象
        auto& manager = dev::device_manager::get_instance();
        try {
            manager.remove_object("test_chip_pca9545", dev::object_type::chip);
        } catch (...) {
            // 忽略删除失败的情况
        }

        // 确保之前的对象已清理
        if (m_pca9545) {
            try {
                // 使用对象路径注销对象（路径必须以 '/' 开头）
                std::string path = std::string(m_pca9545->get_object_path());
                if (!path.empty() && path[0] == '/') {
                    m_test_service->unregister_object(path);
                }
            } catch (...) {
                // 忽略注销失败的情况
            }
            m_pca9545.reset();
        }

        // 等待一下，确保清理完成
        std::this_thread::sleep_for(std::chrono::milliseconds(10));

        // 创建chip对象，供pca9545使用
        mc::mutable_dict chip_csr;
        chip_csr["Address"]     = static_cast<uint32_t>(0x70);
        chip_csr["OffsetWidth"] = static_cast<uint8_t>(1);
        chip_csr["AddrWidth"]   = static_cast<uint8_t>(1);
        m_chip                  = manager.create_chip("test_chip_pca9545", "Chip", chip_csr);
        ASSERT_NE(m_chip, nullptr);

        // 创建pca9545对象，使用唯一的对象名称（基于时间戳和计数器）
        static std::atomic<int> test_counter{0};
        int                     counter = test_counter.fetch_add(1);
        auto                    now     = std::chrono::steady_clock::now();
        auto                    timestamp =
            std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
        std::string unique_name =
            "test_pca9545_" + std::to_string(timestamp) + "_" + std::to_string(counter);

        m_pca9545 = mc::make_shared<dev::pca9545_object>();
        m_pca9545->set_service(m_test_service.get());
        m_pca9545->set_object_name(unique_name);
        // 设置父对象为root_object（必须在注册前设置）
        m_pca9545->set_parent(m_root_object.get());
        m_test_service->register_object(m_pca9545);
    }

    void TearDown() override {
        if (m_pca9545) {
            try {
                // 使用对象路径注销对象（路径必须以 '/' 开头）
                std::string path = std::string(m_pca9545->get_object_path());
                if (!path.empty() && path[0] == '/') {
                    m_test_service->unregister_object(path);
                }
            } catch (...) {
                // 忽略注销失败的情况
            }
            m_pca9545.reset();
        }
        auto& manager = dev::device_manager::get_instance();
        try {
            manager.remove_object("test_chip_pca9545", dev::object_type::chip);
        } catch (...) {
            // 忽略删除失败的情况
        }
    }

    mc::mutable_dict create_test_csr() {
        mc::mutable_dict csr;
        csr["object_name"] = std::string("test_chip_pca9545");
        csr["Address"]     = static_cast<uint32_t>(0x70);
        csr["OffsetWidth"] = static_cast<uint8_t>(1);
        csr["AddrWidth"]   = static_cast<uint8_t>(1);
        return csr;
    }

    mc::dict create_test_connector() {
        return mc::dict{{"SystemId", 1}};
    }

    static mc::shared_ptr<pca9545_test_service> m_test_service;
    static root_object_ptr                      m_root_object;
    std::shared_ptr<dev::chip_base>             m_chip;
    mc::shared_ptr<dev::pca9545_object>         m_pca9545;
};

mc::shared_ptr<pca9545_test_service> Pca9545Test::m_test_service;
root_object_ptr                      Pca9545Test::m_root_object;

// ========================= 基本功能测试 =========================

TEST_F(Pca9545Test, Constructor) {
    dev::pca9545_object pca9545;
    EXPECT_TRUE(true);
}

TEST_F(Pca9545Test, Init_Success) {
    auto csr       = create_test_csr();
    auto connector = create_test_connector();

    bool result = m_pca9545->init(csr, connector);
    EXPECT_TRUE(result);

    // 验证chip已设置
    EXPECT_NE(m_pca9545->m_interface_chip.m_chip, nullptr);
    EXPECT_NE(m_pca9545->m_interface_pca9545.m_chip_pca9545, nullptr);
}

TEST_F(Pca9545Test, Init_EmptyCsr) {
    mc::mutable_dict empty_csr;
    auto             connector = create_test_connector();

    bool result = m_pca9545->init(empty_csr, connector);
    EXPECT_FALSE(result);
}

TEST_F(Pca9545Test, Init_ChipNotFound) {
    mc::mutable_dict csr;
    csr["object_name"] = std::string("non_existent_chip");
    auto connector     = create_test_connector();

    bool result = m_pca9545->init(csr, connector);
    EXPECT_FALSE(result);
}

TEST_F(Pca9545Test, Init_ChipIsNull) {
    // 创建一个chip然后删除它
    auto& manager = dev::device_manager::get_instance();
    manager.remove_object("test_chip_pca9545", dev::object_type::chip);
    m_chip.reset(); // 释放chip引用

    auto csr       = create_test_csr();
    auto connector = create_test_connector();

    bool result = m_pca9545->init(csr, connector);
    EXPECT_FALSE(result);

    // 恢复chip对象，避免TearDown出错
    mc::mutable_dict chip_csr;
    chip_csr["Address"]     = static_cast<uint32_t>(0x70);
    chip_csr["OffsetWidth"] = static_cast<uint8_t>(1);
    chip_csr["AddrWidth"]   = static_cast<uint8_t>(1);
    m_chip                  = manager.create_chip("test_chip_pca9545", "Chip", chip_csr);
}

TEST_F(Pca9545Test, Init_InvalidChipType) {
    // 创建一个bus对象而不是chip对象
    auto&            manager = dev::device_manager::get_instance();
    mc::mutable_dict bus_csr;
    bus_csr["Id"] = static_cast<uint8_t>(1);
    auto bus      = manager.create_bus("test_bus", "I2c", bus_csr);
    ASSERT_NE(bus, nullptr);

    mc::mutable_dict csr;
    csr["object_name"] = std::string("test_bus");
    auto connector     = create_test_connector();

    // 应该失败，因为test_bus是bus而不是chip
    bool result = m_pca9545->init(csr, connector);
    EXPECT_FALSE(result);

    manager.remove_object("test_bus", dev::object_type::bus);
}

TEST_F(Pca9545Test, Init_MissingObjectName) {
    mc::mutable_dict csr;
    // 缺少object_name字段
    csr["Address"] = static_cast<uint32_t>(0x70);
    auto connector = create_test_connector();

    // 应该失败（返回false），因为缺少object_name字段会导致异常被捕获
    bool result = m_pca9545->init(csr, connector);
    EXPECT_FALSE(result);
}

// ========================= 多实例测试 =========================

TEST_F(Pca9545Test, MultiInstance) {
    auto&                                            manager = dev::device_manager::get_instance();
    std::vector<mc::shared_ptr<dev::pca9545_object>> pca9545s;

    // 创建多个pca9545实例
    for (int i = 0; i < 3; ++i) {
        std::string      chip_name = "test_chip_pca9545_" + std::to_string(i);
        mc::mutable_dict chip_csr;
        chip_csr["Address"]     = static_cast<uint32_t>(0x70 + i);
        chip_csr["OffsetWidth"] = static_cast<uint8_t>(1);
        chip_csr["AddrWidth"]   = static_cast<uint8_t>(1);
        auto chip               = manager.create_chip(chip_name, "Chip", chip_csr);
        ASSERT_NE(chip, nullptr);

        auto pca9545 = mc::make_shared<dev::pca9545_object>();
        pca9545->set_service(m_test_service.get());
        pca9545->set_object_name("test_pca9545_" + std::to_string(i));
        // 设置父对象为root_object
        pca9545->set_parent(m_root_object.get());
        m_test_service->register_object(pca9545);

        mc::mutable_dict csr;
        csr["object_name"] = chip_name;
        csr["Address"]     = static_cast<uint32_t>(0x70 + i);
        auto connector     = create_test_connector();

        bool result = pca9545->init(csr, connector);
        EXPECT_TRUE(result);

        pca9545s.push_back(pca9545);
    }

    // 清理
    for (auto& pca9545 : pca9545s) {
        m_test_service->unregister_object(pca9545);
    }
    for (int i = 0; i < 3; ++i) {
        std::string chip_name = "test_chip_pca9545_" + std::to_string(i);
        manager.remove_object(chip_name, dev::object_type::chip);
    }
}

} // namespace test
} // namespace pca9545
} // namespace chip
} // namespace drivers
